﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Windows;
using System.Threading;

namespace HB.MVVM
{
#if NET40 || NET45
    /// <summary>
    /// 可通知的字典,无需初始化属性，可动态增加
    /// </summary>
    /// <typeparam name="TKey">xmal中可自动转化数字、枚举、字符串作为key</typeparam>
    /// <typeparam name="TValue"></typeparam>
    public class ObservableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
    {
        public ObservableDictionary() : base() { }
        public ObservableDictionary(IEnumerable<TKey> defaultItems, TValue defaultValue) : base()
        {
            foreach (var k in defaultItems)
            {
                Add(k, defaultValue);
            }
        }

        Dictionary<TKey, WeakReference> dicAction = null;
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        public event PropertyChangedEventHandler PropertyChanged;
        public event Action<TKey, TValue> OnItemChanged;
        /// <summary>
        /// 为属性更改注册通知事件，如多次注册，只保留最后一个
        /// </summary>
        /// <param name="properityName"></param>
        /// <param name="changedAction"></param>
        public void RegisterChangedEvent(TKey properityName, Action changedAction)
        {
            if (dicAction == null) { dicAction = new Dictionary<TKey, WeakReference>(); }
            WeakReference wr = new WeakReference(changedAction);
            dicAction[properityName] = wr;
        }

        public new TValue this[TKey key]
        {
            get
            {
                if (!ContainsKey(key))
                {
                    Add(key, default(TValue));
                    return default(TValue);
                }
                return this.GetValue(key);

            }
            set
            {
                if (ContainsKey(key))
                {
                    this.SetValue(key, value);
                }
                else
                {
                    Add(key, value);
                }
            }
        }
        /// <summary>
        /// 使用数字索引获得变量
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public TValue this[int index]
        {
            get { return this.GetIndexValue(index); }
            set { this.SetIndexValue(index, value); }
        }
        /// <summary>
        /// 增加一个变量
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public new void Add(TKey key, TValue value)
        {
            base.Add(key, value);
            var kv = this.FindPair(key, out int index);
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, kv, index));
            OnPropertyChanged("Keys");
            OnPropertyChanged("Values");
            OnPropertyChanged("Count");


        }
        /// <summary>
        /// 强制更新所有修改（用于class类型value）
        /// </summary>
        public void NotifyAllChanged()
        {
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            OnPropertyChanged("Keys");
            OnPropertyChanged("Values");
            OnPropertyChanged("Count");
        }
        /// <summary>
        /// 清理所有变量
        /// </summary>
        public new void Clear()
        {
            base.Clear();
            NotifyAllChanged();
        }
        /// <summary>
        /// 移除一个变量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public new bool Remove(TKey key)
        {
            var pair = this.FindPair(key, out int index);
            base.Remove(key);
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, pair, index));
            OnPropertyChanged("Keys");
            OnPropertyChanged("Values");
            OnPropertyChanged("Count");
            return true;
        }

        protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (Thread.CurrentThread.ManagedThreadId == Application.Current.Dispatcher.Thread.ManagedThreadId)
            {
                this.CollectionChanged?.Invoke(this, e);
            }
            else
            {
                Application.Current.Dispatcher.Invoke(new Action(() =>
                {
                    this.CollectionChanged?.Invoke(this, e);
                }));
            }
        }

        protected void OnPropertyChanged(string propertyName)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        /// <summary>
        /// 单独通知某个value变化（用于class类型value）
        /// </summary>
        /// <param name="key"></param>
        public void NotifyItemChanged(TKey key)
        {
            if (base.ContainsKey(key))
            {
                var pair = this.FindPair(key, out int index);
                if (pair.Value.GetType().IsClass)
                {
                    lock (this)
                    {
                        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, pair, index));
                        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, pair, index));
                    }
                    OnPropertyChanged("Values");
                    OnPropertyChanged("Item[]");
                }
                else
                {
                    this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, pair, pair, index));
                }

            }
        }
        #region private方法
        private TValue GetIndexValue(int index)
        {
            for (int i = 0; i < this.Count; i++)
            {
                if (i == index)
                {
                    var pair = this.ElementAt(i);
                    return pair.Value;
                }
            }

            return default(TValue);
        }

        private void SetIndexValue(int index, TValue value)
        {
            try
            {
                var pair = this.ElementAtOrDefault(index);
                SetValue(pair.Key, value);
            }
            catch (Exception)
            {

            }
        }

        private TValue GetValue(TKey key)
        {
            if (ContainsKey(key))
            {
                return base[key];
            }
            else
            {
                return default(TValue);
            }
        }

        private void SetValue(TKey key, TValue value)
        {
            if (ContainsKey(key))
            {
                var pair = this.FindPair(key, out int oldindex);
                base[key] = value;
                var newpair = this.FindPair(key, out int newindex);
                if (value != null && value.GetType().IsClass)
                {

                    this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, pair, oldindex));
                    this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newpair, newindex));

                }
                else
                {
                    this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newpair, pair, newindex));

                }
                OnItemChanged?.Invoke(key, value);
                OnPropertyChanged("Values");
                OnPropertyChanged("Item[]");
                if (dicAction != null && dicAction.ContainsKey(key))
                {
                    if (!pair.Value.Equals(newpair.Value))
                    {
                        var action = dicAction[key].Target as Action;
                        if (action != null)
                        {
                            action.Invoke();
                        }

                    }

                }
            }

        }

        private KeyValuePair<TKey, TValue> FindPair(TKey key, out int index)
        {
            index = 0;
            int count = this.Count;
            var keylist = Keys.ToArray();
            for (int i = 0; i < keylist.Length; i++)
            {

                if (keylist[i].Equals(key))
                {
                    index = i;
                    return new KeyValuePair<TKey, TValue>(key, this[key]);
                }
            }
            return default(KeyValuePair<TKey, TValue>);
        }



        #endregion

    }
#endif
}
